#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sstream>

#include <FifoEscritura.h>
#include <FifoLectura.h>
#include <BaseException.h>

using namespace std;

int calcularRandom ();


int main (int argc, char* argv[])
{
	try
	{
		string nombreFifo = "/tmp/testNamedPipe";
		char projectId = 't';
		int cantCiclos = 3;
		int cantHijos = 2;
		int j = 0;
		pid_t pid = 1;
		
		for( ; j < cantHijos; j++)
		{
			// El padre lanza la cantidad de hijos necesarios
			if ( pid != 0 )
			{
				pid = fork ();
			}
			else
			{
				// Los hijos continuan con el flujo normal
				break;
			}
		}
		
		if ( pid == 0 )
		{
			pid_t pidHijo = getpid();
			cout << "HIJO " << pidHijo << ": Espero a que mi padre cree un fifo" << endl;
			sleep(2);
			
			cout << "HIJO " << pidHijo << ": Intento unirme al fifo de mi padre " << nombreFifo << endl;
			bool protectAccess = true;
			FifoEscritura fifoE(nombreFifo, CreationMode::DEBE_EXISTIR, protectAccess);
			
			cout << "HIJO " << pidHijo << ": Conectado al fifo. Voy a abrirlo" << endl;
			fifoE.abrir();
			cout << "HIJO " << pidHijo << ": Fifo de escritura abierto" << endl;
			
			int i = 0;
			for( ; i < cantCiclos; i++)
			{
				cout << "HIJO " << pidHijo << ": Ciclo numero " << i + 1 << endl;
				
				cout << "HIJO " << pidHijo << ": Intento tomar el lock de escritura" << endl;
				fifoE.lock();
				
				stringstream mensaje;
				mensaje << "Hola pa. Numero " << calcularRandom();
				int msgSize = mensaje.str().size();
				cout << "HIJO " << pidHijo << ": Lock de escritura tomado. Escribo la longitud del mensaje:" << msgSize << endl;
				fifoE.escribir((const void*) &msgSize, sizeof(int));
				cout << "HIJO " << pidHijo << ": Escribo el mensaje:" << mensaje.str() << endl;
				fifoE.escribir((const void*) mensaje.str().c_str(), msgSize);
				
				cout << "HIJO " << pidHijo << ": Mensaje enviado por el fifo. Libero lock de escritura." << endl;
				fifoE.unlock();
				
				cout << "HIJO " << pidHijo << ": Lock de escritura liberado. Espero 2 segundos antes de mandar el proximo mensaje" << endl;
				sleep(2);
			}
			
			cout << "HIJO " << pidHijo << ": Cierro el fifo de escritura" << endl;
			fifoE.cerrar();
			
			cout << "HIJO " << pidHijo << ": Fifo cerrado. Finalizo" << endl;
			return 0;
		}
		else 
		{
			cout << "PADRE: PID:" << getpid() << endl;
			cout << "PADRE: Voy a crear un fifo " << nombreFifo << " para que mi hijo me mande un mensaje" << endl;
			bool protectAccess = false;
			FifoLectura fifoL(nombreFifo, CreationMode::NO_DEBE_EXISTIR, protectAccess);
			
			cout << "PADRE: Fifo creado. Voy a abrir el fifo" << endl;
			fifoL.abrir();
			
			int i = 0;
			for( ; i < cantCiclos*cantHijos; i++)
			{
				cout << "PADRE: Ciclo " << i + 1 << endl;
				cout << "PADRE: Leo la longitud del mensaje que me envian por fifo" << endl;
				const ssize_t buffSize = sizeof(int);
				char buffer[buffSize];
				fifoL.leer((void*)buffer,buffSize);
				int* msgSize = (int*) buffer;
				cout << "PADRE: Longitud del mensaje leido:" << *msgSize << endl;
				
				if(*msgSize > 0)
				{
					cout << "PADRE: Leo el mensaje que me envian por fifo" << endl;
					char buffer2[*msgSize];
					fifoL.leer((void*)buffer2,*msgSize);
					string msgRecibido(buffer2, *msgSize);
					cout << "PADRE: Mensaje leido:" << msgRecibido << endl;
				}
				else
				{
					cout << "PADRE: ERROR: La longitud de mensaje recibida es 0. Ignoro" << endl;
				}
			}
			
			cout << "PADRE: Cierro el fifo" << endl;
			fifoL.cerrar();
			
			cout << "PADRE: Espero a que mis hijos finalicen" << endl;
			int estado;
			while( wait( (void*) &estado) > 0);
			
			cout << "PADRE: Mis hijos finalizaron. Lo hago yo tambien." << endl;
		}
		std::cout << "testFifo OK" << std::endl;
	}
	catch (const BaseException& e) {
		std::cerr << "testFifo FAIL: Error " << e.getCode() << ":" << e.what() << std::endl;
	}
}


int calcularRandom () {
	srand ( time(NULL) );
	int resultado = rand() % 100 + 1;
	return resultado;
}
